import React from 'react';
import { throttle, getPosX } from './utils';
import styles from './index.module.less';

export default function ProgressBar(props) {
    const { audio } = props;
    const progressBarRef = React.useRef();

    const isDraggingRef = React.useRef();
    const [currentTimePos, setCurrentTimePos] = React.useState();
    // 加载进度
    const bufferedTimerRef = React.useRef();
    const [bufferedTimePos, setBufferedTimePos] = React.useState('0%');

    let timeOnMouseMove = 0;

    const handleAudioTimeUpdate = throttle(e => {
        if (isDraggingRef.current) return;
        const { currentTime, duration } = e.target;
        setCurrentTimePos(`${((currentTime / duration) * 100 || 0).toFixed(2)}%`);
    }, 300);

    const getCurrentProgress = e => {
        const isSingleFileProgressiveDownload = audio.src.indexOf('blob:') !== 0;

        if (
            isSingleFileProgressiveDownload &&
            (!audio.src || !Number.isFinite(audio.currentTime) || !progressBarRef.current)
        ) {
            return { currentTime: 0, currentTimePos: '0%' };
        }

        const progressBarRect = progressBarRef.current.getBoundingClientRect();
        const maxRelativePos = progressBarRect.width;
        let relativePos = getPosX(e) - progressBarRect.left;

        if (relativePos < 0) {
            relativePos = 0;
        } else if (relativePos > maxRelativePos) {
            relativePos = maxRelativePos;
        }
        const { duration } = audio;
        const currentTime = (duration * relativePos) / maxRelativePos;
        return { currentTime, currentTimePos: `${((relativePos / maxRelativePos) * 100).toFixed(2)}%` };
    };

    const handleWindowMouseOrTouchMove = e => {
        e.preventDefault();
        e.stopPropagation();
        // Prevent Chrome drag selection bug
        const windowSelection = window.getSelection();
        if (windowSelection && windowSelection.type === 'Range') {
            windowSelection.empty();
        }

        if (isDraggingRef.current) {
            const { currentTime, currentTimePos: currentTimePosValue } = getCurrentProgress(e);
            timeOnMouseMove = currentTime;
            setCurrentTimePos(currentTimePosValue);
        }
    };

    const handleWindowMouseOrTouchUp = e => {
        e.stopPropagation();
        const newTime = timeOnMouseMove;
        audio.currentTime = newTime;

        isDraggingRef.current = false;

        if (e instanceof MouseEvent) {
            window.removeEventListener('mousemove', handleWindowMouseOrTouchMove);
            window.removeEventListener('mouseup', handleWindowMouseOrTouchUp);
        } else {
            window.removeEventListener('touchmove', handleWindowMouseOrTouchMove);
            window.removeEventListener('touchend', handleWindowMouseOrTouchUp);
        }
    };

    const handleMouseDownOrTouchStartProgressBar = e => {
        e.stopPropagation();
        const { currentTime, currentTimePos: currentTimePosValue } = getCurrentProgress(e.nativeEvent);

        if (Number.isFinite(currentTime)) {
            timeOnMouseMove = currentTime;
            isDraggingRef.current = true;
            setCurrentTimePos(currentTimePosValue);
            if (e.nativeEvent instanceof MouseEvent) {
                window.addEventListener('mousemove', handleWindowMouseOrTouchMove);
                window.addEventListener('mouseup', handleWindowMouseOrTouchUp);
            } else {
                window.addEventListener('touchmove', handleWindowMouseOrTouchMove);
                window.addEventListener('touchend', handleWindowMouseOrTouchUp);
            }
        }
    };

    const handleContextMenu = e => {
        e.preventDefault();
    };

    const refreshBuffered = () => {
        try {
            const z = audio.buffered.end(audio.buffered.length - 1);
            if (z >= audio.duration) {
                clearInterval(bufferedTimerRef.current);
                setBufferedTimePos('100%');
            } else {
                setBufferedTimePos(`${((z / audio.duration) * 100).toFixed(2)}%`);
            }
        } catch (e) {
            //
        }
    };

    const handleLoaded = () => {
        if (!bufferedTimerRef.current) {
            refreshBuffered();
            bufferedTimerRef.current = setInterval(() => {
                refreshBuffered();
            }, 1000);
        }
    };

    React.useEffect(() => {
        handleLoaded();
        audio.addEventListener('timeupdate', handleAudioTimeUpdate);
        return () => {
            audio.removeEventListener('timeupdate', handleAudioTimeUpdate);
        };
    }, [audio]);

    return (
        <div
            className={styles.progressBarWrap}
            onMouseDown={handleMouseDownOrTouchStartProgressBar}
            onTouchStart={handleMouseDownOrTouchStartProgressBar}
            onContextMenu={handleContextMenu}
            ref={progressBarRef}
        >
            <div className={styles.progressBar}>
                <div className={styles.progressIndicator} style={{ left: currentTimePos }} />
                <div className={styles.progressBuffered} style={{ width: bufferedTimePos }} />
                <div className={styles.progressFilled} style={{ width: currentTimePos }} />
            </div>
        </div>
    );
}
